home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Games Extra 1996 September
/
Amiga Games Extra CD-ROM 9-1996.iso
/
userbox
/
publicdomain
/
vim-4.2
/
src
/
search.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-09
|
72KB
|
3,138 lines
/* vi:set ts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* search.c: code for normal mode searching commands
*/
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"
#include "ops.h" /* for op_inclusive */
/* modified Henry Spencer's regular expression routines */
#include "regexp.h"
static int inmacro __ARGS((char_u *, char_u *));
static int cls __ARGS((void));
static int skip_chars __ARGS((int, int));
static void back_in_line __ARGS((void));
static void find_first_blank __ARGS((FPOS *));
static void show_pat_in_path __ARGS((char_u *, int,
int, int, FILE *, linenr_t *, long));
static void give_warning __ARGS((char_u *));
static char_u *top_bot_msg = (char_u *)"search hit TOP, continuing at BOTTOM";
static char_u *bot_top_msg = (char_u *)"search hit BOTTOM, continuing at TOP";
/*
* This file contains various searching-related routines. These fall into
* three groups:
* 1. string searches (for /, ?, n, and N)
* 2. character searches within a single line (for f, F, t, T, etc)
* 3. "other" kinds of searches like the '%' command, and 'word' searches.
*/
/*
* String searches
*
* The string search functions are divided into two levels:
* lowest: searchit(); called by do_search() and edit().
* Highest: do_search(); changes curwin->w_cursor, called by normal().
*
* The last search pattern is remembered for repeating the same search.
* This pattern is shared between the :g, :s, ? and / commands.
* This is in myregcomp().
*
* The actual string matching is done using a heavily modified version of
* Henry Spencer's regular expression library.
*/
/*
* Two search patterns are remembered: One for the :substitute command and
* one for other searches. last_pattern points to the one that was
* used the last time.
*/
static char_u *search_pattern = NULL;
static int search_magic = TRUE;
static int search_no_scs = FALSE;
static char_u *subst_pattern = NULL;
static int subst_magic = TRUE;
static int subst_no_scs = FALSE;
static char_u *last_pattern = NULL;
static int last_magic = TRUE;
static int last_no_scs = FALSE;
static char_u *mr_pattern = NULL; /* pattern used by myregcomp() */
static int want_start; /* looking for start of line? */
/*
* Type used by find_pattern_in_path() to remember which included files have
* been searched already.
*/
typedef struct SearchedFile
{
FILE *fp; /* File pointer */
char_u *name; /* Full name of file */
linenr_t lnum; /* Line we were up to in file */
int matched; /* Found a match in this file */
} SearchedFile;
/*
* translate search pattern for vim_regcomp()
*
* sub_cmd == RE_SEARCH: save pat in search_pattern (normal search command)
* sub_cmd == RE_SUBST: save pat in subst_pattern (:substitute command)
* sub_cmd == RE_BOTH: save pat in both patterns (:global command)
* which_pat == RE_SEARCH: use previous search pattern if "pat" is NULL
* which_pat == RE_SUBST: use previous sustitute pattern if "pat" is NULL
* which_pat == RE_LAST: use last used pattern if "pat" is NULL
* options & SEARCH_HIS: put search string in history
* options & SEARCH_KEEP: keep previous search pattern
*
*/
regexp *
myregcomp(pat, sub_cmd, which_pat, options)
char_u *pat;
int sub_cmd;
int which_pat;
int options;
{
rc_did_emsg = FALSE;
reg_magic = p_magic;
if (pat == NULL || *pat == NUL) /* use previous search pattern */
{
if (which_pat == RE_SEARCH)
{
if (search_pattern == NULL)
{
emsg(e_noprevre);
rc_did_emsg = TRUE;
return (regexp *) NULL;
}
pat = search_pattern;
reg_magic = search_magic;
no_smartcase = search_no_scs;
}
else if (which_pat == RE_SUBST)
{
if (subst_pattern == NULL)
{
emsg(e_nopresub);
rc_did_emsg = TRUE;
return (regexp *) NULL;
}
pat = subst_pattern;
reg_magic = subst_magic;
no_smartcase = subst_no_scs;
}
else /* which_pat == RE_LAST */
{
if (last_pattern == NULL)
{
emsg(e_noprevre);
rc_did_emsg = TRUE;
return (regexp *) NULL;
}
pat = last_pattern;
reg_magic = last_magic;
no_smartcase = last_no_scs;
}
}
else if (options & SEARCH_HIS)
add_to_history(1, pat); /* put new pattern in history */
mr_pattern = pat;
/*
* save the currently used pattern in the appropriate place,
* unless the pattern should not be remembered
*/
if (!(options & SEARCH_KEEP))
{
/*
* search or global command
*/
if (sub_cmd == RE_SEARCH || sub_cmd == RE_BOTH)
{
if (search_pattern != pat)
{
vim_free(search_pattern);
search_pattern = strsave(pat);
last_pattern = search_pattern;
search_magic = reg_magic;
last_magic = reg_magic; /* Magic sticks with the r.e. */
search_no_scs = no_smartcase;
last_no_scs = no_smartcase;
}
}
/*
* substitute or global command
*/
if (sub_cmd == RE_SUBST || sub_cmd == RE_BOTH)
{
if (subst_pattern != pat)
{
vim_free(subst_pattern);
subst_pattern = strsave(pat);
last_pattern = subst_pattern;
subst_magic = reg_magic;
last_magic = reg_magic; /* Magic sticks with the r.e. */
subst_no_scs = no_smartcase;
last_no_scs = no_smartcase;
}
}
}
want_start = (*pat == '^'); /* looking for start of line? */
set_reg_ic(pat); /* tell the vim_regexec routine how to search */
return vim_regcomp(pat);
}
/*
* Set reg_ic according to p_ic, p_scs and the search pattern.
*/
void
set_reg_ic(pat)
char_u *pat;
{
char_u *p;
reg_ic = p_ic;
if (!no_smartcase && p_scs
#ifdef INSERT_EXPAND
&& !(ctrl_x_mode && curbuf->b_p_inf)
#endif
)
{
/* don't ignore case if pattern has uppercase */
for (p = pat; *p; )
if (isupper(*p++))
reg_ic = FALSE;
}
no_smartcase = FALSE;
}
/*
* lowest level search function.
* Search for 'count'th occurrence of 'str' in direction 'dir'.
* Start at position 'pos' and return the found position in 'pos'.
*
* if (options & SEARCH_MSG) == 0 don't give any messages
* if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
* if (options & SEARCH_MSG) == SEARCH_MSG give all messages
* if (options & SEARCH_HIS) put search pattern in history
* if (options & SEARCH_END) return position at end of match
* if (options & SEARCH_START) accept match at pos itself
* if (options & SEARCH_KEEP) keep previous search pattern
*
* Return OK for success, FAIL for failure.
*/
int
searchit(pos, dir, str, count, options, which_pat)
FPOS *pos;
int dir;
char_u *str;
long count;
int options;
int which_pat;
{
int found;
linenr_t lnum; /* no init to shut up Apollo cc */
regexp *prog;
char_u *ptr;
register char_u *match = NULL, *matchend = NULL; /* init for GCC */
int loop;
FPOS start_pos;
int at_first_line;
int extra_col;
int match_ok;
char_u *p;
if ((prog = myregcomp(str, RE_SEARCH, which_pat,
(options & (SEARCH_HIS + SEARCH_KEEP)))) == NULL)
{
if ((options & SEARCH_MSG) && !rc_did_emsg)
emsg2((char_u *)"Invalid search string: %s", mr_pattern);
return FAIL;
}
if (options & SEARCH_START)
extra_col = 0;
else
extra_col = 1;
/*
* find the string
*/
do /* loop for count */
{
start_pos = *pos; /* remember start pos for detecting no match */
found = 0; /* default: not found */
/*
* Start searching in current line, unless searching backwards and
* we're in column 0 or searching forward and we're past the end of
* the line
*/
if (dir == BACKWARD && start_pos.col == 0)
{
lnum = pos->lnum - 1;
at_first_line = FALSE;
}
else
{
lnum = pos->lnum;
at_first_line = TRUE;
}
for (loop = 0; loop <= 1; ++loop) /* loop twice if 'wrapscan' set */
{
for ( ; lnum > 0 && lnum <= curbuf->b_ml.ml_line_count;
lnum += dir, at_first_line = FALSE)
{
ptr = ml_get(lnum);
/* forward search, first line */
if (dir == FORWARD && at_first_line && start_pos.col > 0 &&
want_start)
continue; /* match not possible */
/*
* Look for a match somewhere in the line.
*/
if (vim_regexec(prog, ptr, TRUE))
{
match = prog->startp[0];
matchend = prog->endp[0];
/*
* Forward search in the first line: match should be after
* the start position. If not, continue at the end of the
* match (this is vi compatible).
*/
if (dir == FORWARD && at_first_line)
{
match_ok = TRUE;
/*
* When *match == NUL the cursor will be put one back
* afterwards, compare with that position, otherwise
* "/$" will get stuck on end of line. Same for
* matchend.
*/
while ((options & SEARCH_END) ?
((int)(matchend - ptr) - 1 - (int)(*matchend == NUL) <
(int)start_pos.col + extra_col) :
((int)(match - ptr) - (int)(*match == NUL) <
(int)start_pos.col + extra_col))
{
/*
* If vi-compatible searching, continue at the end
* of the match, otherwise continue one position
* forward.
*/
if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
{
p = matchend;
if (match == p && *p != NUL)
++p;
}
else
{
p = match;
if (*p != NUL)
++p;
}
if (*p != NUL && vim_regexec(prog, p, FALSE))
{
match = prog->startp[0];
matchend = prog->endp[0];
}
else
{
match_ok = FALSE;
break;
}
}
if (!match_ok)
continue;
}
if (dir == BACKWARD && !want_start)
{
/*
* Now, if there are multiple matches on this line,
* we have to get the last one. Or the last one before
* the cursor, if we're on that line.
* When putting the new cursor at the end, compare
* relative to the end of the match.
*/
match_ok = FALSE;
for (;;)
{
if (!at_first_line || ((options & SEARCH_END) ?
((prog->endp[0] - ptr) - 1 + extra_col
<= (int)start_pos.col) :
((prog->startp[0] - ptr) + extra_col
<= (int)start_pos.col)))
{
match_ok = TRUE;
match = prog->startp[0];
matchend = prog->endp[0];
}
else
break;
/*
* If vi-compatible searching, continue at the end
* of the match, otherwise continue one position
* forward.
*/
if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
{
p = matchend;
if (p == match && *p != NUL)
++p;
}
else
{
p = match;
if (*p != NUL)
++p;
}
if (*p == NUL || !vim_regexec(prog, p, (int)FALSE))
break;
}
/*
* If there is only a match after the cursor, skip
* this match.
*/
if (!match_ok)
continue;
}
pos->lnum = lnum;
if (options & SEARCH_END && !(options & SEARCH_NOOF))
pos->col = (int) (matchend - ptr - 1);
else
pos->col = (int) (match - ptr);
found = 1;
break;
}
line_breakcheck(); /* stop if ctrl-C typed */
if (got_int)
break;
if (loop && lnum == start_pos.lnum)
break; /* if second loop, stop where started */
}
at_first_line = FALSE;
/*
* stop the search if wrapscan isn't set, after an interrupt and
* after a match
*/
if (!p_ws || got_int || found)
break;
/*
* If 'wrapscan' is set we continue at the other end of the file.
* If 'shortmess' does not contain 's', we give a message.
* This message is also remembered in keep_msg for when the screen
* is redrawn. The keep_msg is cleared whenever another message is
* written.
*/
if (dir == BACKWARD) /* start second loop at the other end */
{
lnum = curbuf->b_ml.ml_line_count;
if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
give_warning(top_bot_msg);
}
else
{
lnum = 1;
if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
give_warning(bot_top_msg);
}
}
if (got_int)
break;
}
while (--count > 0 && found); /* stop after count matches or no match */
vim_free(prog);
if (!found) /* did not find it */
{
if (got_int)
emsg(e_interr);
else if ((options & SEARCH_MSG) == SEARCH_MSG)
{
if (p_ws)
EMSG2("Pattern not found: %s", mr_pattern);
else if (lnum == 0)
EMSG2("search hit TOP without match for: %s", mr_pattern);
else
EMSG2("search hit BOTTOM without match for: %s", mr_pattern);
}
return FAIL;
}
search_match_len = matchend - match;
return OK;
}
/*
* Highest level string search function.
* Search for the 'count'th occurence of string 'str' in direction 'dirc'
* If 'dirc' is 0: use previous dir.
* If 'str' is NULL or empty : use previous string.
* If 'options & SEARCH_REV' : go in reverse of previous dir.
* If 'options & SEARCH_ECHO': echo the search command and handle options
* If 'options & SEARCH_MSG' : may give error message
* If 'options & SEARCH_OPT' : interpret optional flags
* If 'options & SEARCH_HIS' : put search pattern in history
* If 'options & SEARCH_NOOF': don't add offset to position
* If 'options & SEARCH_MARK': set previous context mark
* If 'options & SEARCH_KEEP': keep previous search pattern
*
* Careful: If lastoffline == TRUE and lastoff == 0 this makes the
* movement linewise without moving the match position.
*
* return 0 for failure, 1 for found, 2 for found and line offset added
*/
int
do_search(dirc, str, count, options)
int dirc;
char_u *str;
long count;
int options;
{
FPOS pos; /* position of the last match */
char_u *searchstr;
static int lastsdir = '/'; /* previous search direction */
static int lastoffline;/* previous/current search has line offset */
static int lastend; /* previous/current search set cursor at end */
static long lastoff; /* previous/current line or char offset */
int old_lastsdir;
int old_lastoffline;
int old_lastend;
long old_lastoff;
int retval; /* Return value */
register char_u *p;
register long c;
char_u *dircp;
int i = 0; /* init for GCC */
/*
* A line offset is not remembered, this is vi compatible.
*/
if (lastoffline && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
{
lastoffline = FALSE;
lastoff = 0;
}
/*
* Save the values for when (options & SEARCH_KEEP) is used.
* (there is no "if ()" around this because gcc wants them initialized)
*/
old_lastsdir = lastsdir;
old_lastoffline = lastoffline;
old_lastend = lastend;
old_lastoff = lastoff;
pos = curwin->w_cursor; /* start searching at the cursor position */
/*
* Find out the direction of the search.
*/
if (dirc == 0)
dirc = lastsdir;
else
lastsdir = dirc;
if (options & SEARCH_REV)
{
#ifdef WIN32
/* There is a bug in the Visual C++ 2.2 compiler which means that
* dirc always ends up being '/' */
dirc = (dirc == '/') ? '?' : '/';
#else
if (dirc == '/')
dirc = '?';
else
dirc = '/';
#endif
}
/*
* Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
*/
for (;;)
{
searchstr = str;
dircp = NULL;
/* use previous pattern */
if (str == NULL || *str == NUL || *str == dirc)
{
if (search_pattern == NULL) /* no previous pattern */
{
emsg(e_noprevre);
retval = 0;
goto end_do_search;
}
searchstr = (char_u *)""; /* make myregcomp() use search_pattern */
}
if (str != NULL && *str != NUL) /* look for (new) offset */
{
/*
* Find end of regular expression.
* If there is a matching '/' or '?', toss it.
*/
p = skip_regexp(str, dirc);
if (*p == dirc)
{
dircp = p; /* remember where we put the NUL */
*p++ = NUL;
}
lastoffline = FALSE;
lastend = FALSE;
lastoff = 0;
/*
* Check for a line offset or a character offset.
* For get_address (echo off) we don't check for a character
* offset, because it is meaningless and the 's' could be a
* substitute command.
*/
if (*p == '+' || *p == '-' || isdigit(*p))
lastoffline = TRUE;
else if ((options & SEARCH_OPT) &&
(*p == 'e' || *p == 's' || *p == 'b'))
{
if (*p == 'e') /* end */
lastend = SEARCH_END;
++p;
}
if (isdigit(*p) || *p == '+' || *p == '-') /* got an offset */
{
if (isdigit(*p) || isdigit(*(p + 1)))
lastoff = atol((char *)p); /* 'nr' or '+nr' or '-nr' */
else if (*p == '-') /* single '-' */
lastoff = -1;
else /* single '+' */
lastoff = 1;
++p;
while (isdigit(*p)) /* skip number */
++p;
}
searchcmdlen = p - str; /* compute length of search command
for get_address() */
str = p; /* put str after search command */
}
if (options & SEARCH_ECHO)
{
msg_start();
msg_outchar(dirc);
msg_outtrans(*searchstr == NUL ? search_pattern : searchstr);
if (lastoffline || lastend || lastoff)
{
msg_outchar(dirc);
if (lastend)
msg_outchar('e');
else if (!lastoffline)
msg_outchar('s');
if (lastoff < 0)
{
msg_outchar('-');
msg_outnum((long)-lastoff);
}
else if (lastoff > 0 || lastoffline)
{
msg_outchar('+');
msg_outnum((long)lastoff);
}
}
msg_clr_eos();
(void)msg_check();
gotocmdline(FALSE);
flushbuf();
}
/*
* If there is a character offset, subtract it from the current
* position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
* This is not done for a line offset, because then we would not be vi
* compatible.
*/
if (!lastoffline && lastoff)
{
if (lastoff > 0)
{
c = lastoff;
while (c--)
if ((i = dec(&pos)) != 0)
break;
if (i == -1) /* at start of buffer */
goto_endofbuf(&pos);
}
else
{
c = -lastoff;
while (c--)
if ((i = inc(&pos)) != 0)
break;
if (i == -1) /* at end of buffer */
{
pos.lnum = 1;
pos.col = 0;
}
}
}
c = searchit(&pos, dirc == '/' ? FORWARD : BACKWARD, searchstr, count,
lastend + (options &
(SEARCH_KEEP + SEARCH_HIS + SEARCH_MSG +
((str != NULL && *str == ';') ? 0 : SEARCH_NOOF))),
2);
if (dircp != NULL)
*dircp = dirc; /* put second '/' or '?' back for normal() */
if (c == FAIL)
{
retval = 0;
goto end_do_search;
}
if (lastend)
op_inclusive = TRUE; /* 'e' includes last character */
retval = 1; /* pattern found */
/*
* Add character and/or line offset
*/
if (!(options & SEARCH_NOOF) || *str == ';')
{
if (lastoffline) /* Add the offset to the line number. */
{
c = pos.lnum + lastoff;
if (c < 1)
pos.lnum = 1;
else if (c > curbuf->b_ml.ml_line_count)
pos.lnum = curbuf->b_ml.ml_line_count;
else
pos.lnum = c;
pos.col = 0;
retval = 2; /* pattern found, line offset added */
}
else
{
if (lastoff > 0) /* to the right, check for end of line */
{
p = ml_get_pos(&pos) + 1;
c = lastoff;
while (c-- && *p++ != NUL)
++pos.col;
}
else /* to the left, check for start of line */
{
if ((c = pos.col + lastoff) < 0)
c = 0;
pos.col = c;
}
}
}
/*
* The search command can be followed by a ';' to do another search.
* For example: "/pat/;/foo/+3;?bar"
* This is like doing another search command, except:
* - The remembered direction '/' or '?' is from the first search.
* - When an error happens the cursor isn't moved at all.
* Don't do this when called by get_address() (it handles ';' itself).
*/
if (!(options & SEARCH_OPT) || str == NULL || *str != ';')
break;
dirc = *++str;
if (dirc != '?' && dirc != '/')
{
retval = 0;
EMSG("Expected '?' or '/' after ';'");
goto end_do_search;
}
++str;
}
if (options & SEARCH_MARK)
setpcmark();
curwin->w_cursor = pos;
curwin->w_set_curswant = TRUE;
end_do_search:
if (options & SEARCH_KEEP)
{
lastsdir = old_lastsdir;
lastoffline = old_lastoffline;
lastend = old_lastend;
lastoff = old_lastoff;
}
return retval;
}
/*
* search_for_exact_line(pos, dir, pat)
*
* Search for a line starting with the given pattern (ignoring leading
* white-space), starting from pos and going in direction dir. pos will
* contain the position of the match found. Blank lines will never match.
* Return OK for success, or FAIL if no line found.
*/
int
search_for_exact_line(pos, dir, pat)
FPOS *pos;
int dir;
char_u *pat;
{
linenr_t start = 0;
char_u *ptr;
char_u *p;
if (curbuf->b_ml.ml_line_count == 0)
return FAIL;
for (;;)
{
pos->lnum += dir;
if (pos->lnum < 1)
{
if (p_ws)
{
pos->lnum = curbuf->b_ml.ml_line_count;
if (!shortmess(SHM_SEARCH))
give_warning(top_bot_msg);
}
else
{
pos->lnum = 1;
break;
}
}
else if (pos->lnum > curbuf->b_ml.ml_line_count)
{
if (p_ws)
{
pos->lnum = 1;
if (!shortmess(SHM_SEARCH))
give_warning(bot_top_msg);
}
else
{
pos->lnum = 1;
break;
}
}
if (pos->lnum == start)
break;
if (start == 0)
start = pos->lnum;
ptr = ml_get(pos->lnum);
p = skipwhite(ptr);
pos->col = p - ptr;
if (*p != NUL && STRNCMP(p, pat, STRLEN(pat)) == 0)
return OK;
else if (*p != NUL && p_ic)
{
ptr = pat;
while (*p && TO_LOWER(*p) == TO_LOWER(*ptr))
{
++p;
++ptr;
}
if (*ptr == NUL)
return OK;
}
}
return FAIL;
}
/*
* Character Searches
*/
/*
* searchc(c, dir, type, count)
*
* Search for character 'c', in direction 'dir'. If 'type' is 0, move to the
* position of the character, otherwise move to just before the char.
* Repeat this 'count' times.
*/
int
searchc(c, dir, type, count)
int c;
register int dir;
int type;
long count;
{
static int lastc = NUL; /* last character searched for */
static int lastcdir; /* last direction of character search */
static int lastctype; /* last type of search ("find" or "to") */
register int col;
char_u *p;
int len;
if (c != NUL) /* normal search: remember args for repeat */
{
if (!KeyStuffed) /* don't remember when redoing */
{
lastc = c;
lastcdir = dir;
lastctype = type;
}
}
else /* repeat previous search */
{
if (lastc == NUL)
return FALSE;
if (dir) /* repeat in opposite direction */
dir = -lastcdir;
else
dir = lastcdir;
type = lastctype;
c = lastc;
}
p = ml_get_curline();
col = curwin->w_cursor.col;
len = STRLEN(p);
while (count--)
{
for (;;)
{
if ((col += dir) < 0 || col >= len)
return FALSE;
if (p[col] == c)
break;
}
}
if (type)
col -= dir;
curwin->w_cursor.col = col;
return TRUE;
}
/*
* "Other" Searches
*/
/*
* findmatch - find the matching paren or brace
*
* Improvement over vi: Braces inside quotes are ignored.
*/
FPOS *
findmatch(initc)
int initc;
{
return findmatchlimit(initc, 0, 0);
}
/*
* findmatchlimit -- find the matching paren or brace, if it exists within
* maxtravel lines of here. A maxtravel of 0 means search until you fall off
* the edge of the file.
*
* flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
* FM_FORWARD search forwards (when initc is '/', '*' or '#')
* FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
* FM_SKIPCOMM skip comments (not implemented yet!)
*/
FPOS *
findmatchlimit(initc, flags, maxtravel)
int initc;
int flags;
int maxtravel;
{
static FPOS pos; /* current search position */
int findc; /* matching brace */
int c;
int count = 0; /* cumulative number of braces */
int idx = 0; /* init for gcc */
static char_u table[6] = {'(', ')', '[', ']', '{', '}'};
#ifdef RIGHTLEFT
static char_u table_rl[6] = {')', '(', ']', '[', '}', '{'};
#endif
int inquote = FALSE; /* TRUE when inside quotes */
register char_u *linep; /* pointer to current line */
char_u *ptr;
int do_quotes; /* check for quotes in current line */
int at_start; /* do_quotes value at start position */
int hash_dir = 0; /* Direction searched for # things */
int comment_dir = 0; /* Direction searched for comments */
FPOS match_pos; /* Where last slash-star was found */
int start_in_quotes; /* start position is in quotes */
int traveled = 0; /* how far we've searched so far */
int ignore_cend = FALSE; /* ignore comment end */
int cpo_match; /* vi compatible matching */
int dir; /* Direction to search */
pos = curwin->w_cursor;
linep = ml_get(pos.lnum);
cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
/* Direction to search when initc is '/', '*' or '#' */
if (flags & FM_BACKWARD)
dir = BACKWARD;
else if (flags & FM_FORWARD)
dir = FORWARD;
else
dir = 0;
/*
* if initc given, look in the table for the matching character
* '/' and '*' are special cases: look for start or end of comment.
* When '/' is used, we ignore running backwards into an star-slash, for
* "[*" command, we just want to find any comment.
*/
if (initc == '/' || initc == '*')
{
comment_dir = dir;
if (initc == '/')
ignore_cend = TRUE;
idx = (dir == FORWARD) ? 0 : 1;
initc = NUL;
}
else if (initc != '#' && initc != NUL)
{
for (idx = 0; idx < 6; ++idx)
#ifdef RIGHTLEFT
if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
{
initc = (curwin->w_p_rl ? table_rl : table)[idx = idx ^ 1];
#else
if (table[idx] == initc)
{
initc = table[idx = idx ^ 1];
#endif
break;
}
if (idx == 6) /* invalid initc! */
return NULL;
}
/*
* Either initc is '#', or no initc was given and we need to look under the
* cursor.
*/
else
{
if (initc == '#')
{
hash_dir = dir;
}
else
{
/*
* initc was not given, must look for something to match under
* or near the cursor.
*/
if (linep[0] == '#' && pos.col == 0)
{
/* If it's not #if, #else etc, we should look for a brace
* instead */
for (c = 1; vim_iswhite(linep[c]); c++)
;
if (STRNCMP(linep + c, "if", (size_t)2) == 0 ||
STRNCMP(linep + c, "endif", (size_t)5) == 0 ||
STRNCMP(linep + c, "el", (size_t)2) == 0)
hash_dir = 1;
}
/*
* Are we on a comment?
*/
else if (linep[pos.col] == '/')
{
if (linep[pos.col + 1] == '*')
{
comment_dir = 1;
idx = 0;
pos.col++;
}
else if (pos.col > 0 && linep[pos.col - 1] == '*')
{
comment_dir = -1;
idx = 1;
pos.col--;
}
}
else if (linep[pos.col] == '*')
{
if (linep[pos.col + 1] == '/')
{
comment_dir = -1;
idx = 1;
}
else if (pos.col > 0 && linep[pos.col - 1] == '/')
{
comment_dir = 1;
idx = 0;
}
}
/*
* If we are not on a comment or the # at the start of a line, then
* look for brace anywhere on this line after the cursor.
*/
if (!hash_dir && !comment_dir)
{
/*
* find the brace under or after the cursor
*/
linep = ml_get(pos.lnum);
idx = 6; /* error if this line is empty */
for (;;)
{
initc = linep[pos.col];
if (initc == NUL)
break;
for (idx = 0; idx < 6; ++idx)
#ifdef RIGHTLEFT
if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
#else
if (table[idx] == initc)
#endif
break;
if (idx != 6)
break;
++pos.col;
}
if (idx == 6)
{
if (linep[0] == '#')
hash_dir = 1;
else
return NULL;
}
}
}
if (hash_dir)
{
/*
* Look for matching #if, #else, #elif, or #endif
*/
op_motion_type = MLINE; /* Linewise for this case only */
if (initc != '#')
{
ptr = skipwhite(linep + 1);
if (STRNCMP(ptr, "if", (size_t)2) == 0 ||
STRNCMP(ptr, "el", (size_t)2) == 0)
hash_dir = 1;
else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
hash_dir = -1;
else
return NULL;
}
pos.col = 0;
while (!got_int)
{
if (hash_dir > 0)
{
if (pos.lnum == curbuf->b_ml.ml_line_count)
break;
}
else if (pos.lnum == 1)
break;
pos.lnum += hash_dir;
linep = ml_get(pos.lnum);
line_breakcheck();
if (linep[0] != '#')
continue;
ptr = linep + 1;
while (*ptr == ' ' || *ptr == TAB)
ptr++;
if (hash_dir > 0)
{
if (STRNCMP(ptr, "if", (size_t)2) == 0)
count++;
else if (STRNCMP(ptr, "el", (size_t)2) == 0)
{
if (count == 0)
return &pos;
}
else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
{
if (count == 0)
return &pos;
count--;
}
}
else
{
if (STRNCMP(ptr, "if", (size_t)2) == 0)
{
if (count == 0)
return &pos;
count--;
}
else if (initc == '#' && STRNCMP(ptr, "el", (size_t)2) == 0)
{
if (count == 0)
return &pos;
}
else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
count++;
}
}
return NULL;
}
}
#ifdef RIGHTLEFT
findc = (curwin->w_p_rl ? table_rl : table)[idx ^ 1];
#else
findc = table[idx ^ 1]; /* get matching brace */
#endif
idx &= 1;
do_quotes = -1;
start_in_quotes = MAYBE;
while (!got_int)
{
/*
* Go to the next position, forward or backward. We could use
* inc() and dec() here, but that is much slower
*/
if (idx) /* backward search */
{
if (pos.col == 0) /* at start of line, go to prev. one */
{
if (pos.lnum == 1) /* start of file */
break;
--pos.lnum;
if (maxtravel && traveled++ > maxtravel)
break;
linep = ml_get(pos.lnum);
pos.col = STRLEN(linep); /* put pos.col on trailing NUL */
do_quotes = -1;
line_breakcheck();
}
else
--pos.col;
}
else /* forward search */
{
if (linep[pos.col] == NUL) /* at end of line, go to next one */
{
if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
break;
++pos.lnum;
if (maxtravel && traveled++ > maxtravel)
break;
linep = ml_get(pos.lnum);
pos.col = 0;
do_quotes = -1;
line_breakcheck();
}
else
++pos.col;
}
/*
* If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
*/
if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
(linep[0] == '{' || linep[0] == '}'))
{
if (linep[0] == findc && count == 0) /* match! */
return &pos;
break; /* out of scope */
}
if (comment_dir)
{
/* Note: comments do not nest, and we ignore quotes in them */
if (comment_dir == 1)
{
if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
{
pos.col++;
return &pos;
}
}
else /* Searching backwards */
{
/*
* A comment may contain slash-star, it may also start or end
* with slash-star-slash. I'm not using real examples though
* because "gcc -Wall" would complain -- webb
*/
if (pos.col == 0)
continue;
else if (linep[pos.col - 1] == '/' && linep[pos.col] == '*')
{
count++;
match_pos = pos;
match_pos.col--;
}
else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
{
if (count > 0)
pos = match_pos;
else if (pos.col > 1 && linep[pos.col - 2] == '/')
pos.col -= 2;
else if (ignore_cend)
continue;
else
return NULL;
return &pos;
}
}
continue;
}
/*
* If smart matching ('cpoptions' does not contain '%'), braces inside
* of quotes are ignored, but only if there is an even number of
* quotes in the line.
*/
if (cpo_match)
do_quotes = 0;
else if (do_quotes == -1)
{
/*
* count the number of quotes in the line, skipping \" and '"'
*/
at_start = do_quotes;
for (ptr = linep; *ptr; ++ptr)
{
if (ptr == linep + curwin->w_cursor.col)
at_start = (do_quotes & 1);
if (*ptr == '"' && (ptr == linep || ptr[-1] != '\\') &&
(ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
++do_quotes;
}
do_quotes &= 1; /* result is 1 with even number of quotes */
/*
* If we find an uneven count, check current line and previous
* one for a '\' at the end.
*/
if (!do_quotes)
{
inquote = FALSE;
if (ptr[-1] == '\\')
{
do_quotes = 1;
if (start_in_quotes == MAYBE)
{
inquote = !at_start;
if (inquote)
start_in_quotes = TRUE;
}
else if (idx) /* backward search */
inquote = TRUE;
}
if (pos.lnum > 1)
{
ptr = ml_get(pos.lnum - 1);
if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
{
do_quotes = 1;
if (start_in_quotes == MAYBE)
{
inquote = at_start;
if (inquote)
start_in_quotes = TRUE;
}
else if (!idx) /* forward search */
inquote = TRUE;
}
}
}
}
if (start_in_quotes == MAYBE)
start_in_quotes = FALSE;
/*
* If 'smartmatch' is set:
* Things inside quotes are ignored by setting 'inquote'. If we
* find a quote without a preceding '\' invert 'inquote'. At the
* end of a line not ending in '\' we reset 'inquote'.
*
* In lines with an uneven number of quotes (without preceding '\')
* we do not know which part to ignore. Therefore we only set
* inquote if the number of quotes in a line is even, unless this
* line or the previous one ends in a '\'. Complicated, isn't it?
*/
switch (c = linep[pos.col])
{
case NUL:
/* at end of line without trailing backslash, reset inquote */
if (pos.col == 0 || linep[pos.col - 1] != '\\')
{
inquote = FALSE;
start_in_quotes = FALSE;
}
break;
case '"':
/* a quote that is preceded with a backslash is ignored */
if (do_quotes && (pos.col == 0 || linep[pos.col - 1] != '\\'))
{
inquote = !inquote;
start_in_quotes = FALSE;
}
break;
/*
* If smart matching ('cpoptions' does not contain '%'):
* Skip things in single quotes: 'x' or '\x'. Be careful for single
* single quotes, eg jon's. Things like '\233' or '\x3f' are not
* skipped, there is never a brace in them.
*/
case '\'':
if (vim_strchr(p_cpo, CPO_MATCH) == NULL)
{
if (idx) /* backward search */
{
if (pos.col > 1)
{
if (linep[pos.col - 2] == '\'')
pos.col -= 2;
else if (linep[pos.col - 2] == '\\' &&
pos.col > 2 && linep[pos.col - 3] == '\'')
pos.col -= 3;
}
}
else if (linep[pos.col + 1]) /* forward search */
{
if (linep[pos.col + 1] == '\\' &&
linep[pos.col + 2] && linep[pos.col + 3] == '\'')
pos.col += 3;
else if (linep[pos.col + 2] == '\'')
pos.col += 2;
}
}
break;
default:
/* Check for match outside of quotes, and inside of
* quotes when the start is also inside of quotes */
if (!inquote || start_in_quotes == TRUE)
{
if (c == initc)
count++;
else if (c == findc)
{
if (count == 0)
return &pos;
count--;
}
}
}
}
if (comment_dir == -1 && count > 0)
{
pos = match_pos;
return &pos;
}
return (FPOS *) NULL; /* never found it */
}
/*
* Move cursor briefly to character matching the one under the cursor.
* Show the match only if it is visible on the screen.
*/
void
showmatch()
{
FPOS *lpos, csave;
colnr_t vcol;
if ((lpos = findmatch(NUL)) == NULL) /* no match, so beep */
beep_flush();
else if (lpos->lnum >= curwin->w_topline)
{
if (!curwin->w_p_wrap)
getvcol(curwin, lpos, NULL, &vcol, NULL);
if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol &&
vcol < curwin->w_leftcol + Columns))
{
updateScreen(VALID_TO_CURSCHAR); /* show the new char first */
csave = curwin->w_cursor;
curwin->w_cursor = *lpos; /* move to matching char */
cursupdate();
showruler(0);
setcursor();
cursor_on(); /* make sure that the cursor is shown */
flushbuf();
/*
* brief pause, unless 'm' is present in 'cpo' and a character is
* available.
*/
if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
mch_delay(500L, TRUE);
else if (!char_avail())
mch_delay(500L, FALSE);
curwin->w_cursor = csave; /* restore cursor position */
cursupdate();
}
}
}
/*
* findsent(dir, count) - Find the start of the next sentence in direction
* 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white
* space or a line break. Also stop at an empty line.
* Return OK if the next sentence was found.
*/
int
findsent(dir, count)
int dir;
long count;
{
FPOS pos, tpos;
register int c;
int (*func) __PARMS((FPOS *));
int startlnum;
int noskip = FALSE; /* do not skip blanks */
pos = curwin->w_cursor;
if (dir == FORWARD)
func = incl;
else
func = decl;
while (count--)
{
/*
* if on an empty line, skip upto a non-empty line
*/
if (gchar(&pos) == NUL)
{
do
if ((*func)(&pos) == -1)
break;
while (gchar(&pos) == NUL);
if (dir == FORWARD)
goto found;
}
/*
* if on the start of a paragraph or a section and searching forward,
* go to the next line
*/
else if (dir == FORWARD && pos.col == 0 &&
startPS(pos.lnum, NUL, FALSE))
{
if (pos.lnum == curbuf->b_ml.ml_line_count)
return FAIL;
++pos.lnum;
goto found;
}
else if (dir == BACKWARD)
decl(&pos);
/* go back to the previous non-blank char */
while ((c = gchar(&pos)) == ' ' || c == '\t' ||
(dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL))
{
if (decl(&pos) == -1)
break;
/* when going forward: Stop in front of empty line */
if (lineempty(pos.lnum) && dir == FORWARD)
{
incl(&pos);
goto found;
}
}
/* remember the line where the search started */
startlnum = pos.lnum;
for (;;) /* find end of sentence */
{
c = gchar(&pos);
if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
{
if (dir == BACKWARD && pos.lnum != startlnum)
++pos.lnum;
break;
}
if (c == '.' || c == '!' || c == '?')
{
tpos = pos;
do
if ((c = inc(&tpos)) == -1)
break;
while (vim_strchr((char_u *)")]\"'", c = gchar(&tpos)) != NULL);
if (c == -1 || c == ' ' || c == '\t' || c == NUL)
{
pos = tpos;
if (gchar(&pos) == NUL) /* skip NUL at EOL */
inc(&pos);
break;
}
}
if ((*func)(&pos) == -1)
{
if (count)
return FAIL;
noskip = TRUE;
break;
}
}
found:
/* skip white space */
while (!noskip && ((c = gchar(&pos)) == ' ' || c == '\t'))
if (incl(&pos) == -1)
break;
}
setpcmark();
curwin->w_cursor = pos;
return OK;
}
/*
* findpar(dir, count, what) - Find the next paragraph in direction 'dir'
* Paragraphs are currently supposed to be separated by empty lines.
* Return TRUE if the next paragraph was found.
* If 'what' is '{' or '}' we go to the next section.
* If 'both' is TRUE also stop at '}'.
*/
int
findpar(dir, count, what, both)
register int dir;
long count;
int what;
int both;
{
register linenr_t curr;
int did_skip; /* TRUE after separating lines have
been skipped */
int first; /* TRUE on first line */
curr = curwin->w_cursor.lnum;
while (count--)
{
did_skip = FALSE;
for (first = TRUE; ; first = FALSE)
{
if (*ml_get(curr) != NUL)
did_skip = TRUE;
if (!first && did_skip && startPS(curr, what, both))
break;
if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
{
if (count)
return FALSE;
curr -= dir;
break;
}
}
}
setpcmark();
if (both && *ml_get(curr) == '}') /* include line with '}' */
++curr;
curwin->w_cursor.lnum = curr;
if (curr == curbuf->b_ml.ml_line_count)
{
if ((curwin->w_cursor.col = STRLEN(ml_get(curr))) != 0)
{
--curwin->w_cursor.col;
op_inclusive = TRUE;
}
}
else
curwin->w_cursor.col = 0;
return TRUE;
}
/*
* check if the string 's' is a nroff macro that is in option 'opt'
*/
static int
inmacro(opt, s)
char_u *opt;
register char_u *s;
{
register char_u *macro;
for (macro = opt; macro[0]; ++macro)
{
if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ') &&
(macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
break;
++macro;
if (macro[0] == NUL)
break;
}
return (macro[0] != NUL);
}
/*
* startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
* If 'para' is '{' or '}' only check for sections.
* If 'both' is TRUE also stop at '}'
*/
int
startPS(lnum, para, both)
linenr_t lnum;
int para;
int both;
{
register char_u *s;
s = ml_get(lnum);
if (*s == para || *s == '\f' || (both && *s == '}'))
return TRUE;
if (*s == '.' && (inmacro(p_sections, s + 1) ||
(!para && inmacro(p_para, s + 1))))
return TRUE;
return FALSE;
}
/*
* The following routines do the word searches performed by the 'w', 'W',
* 'b', 'B', 'e', and 'E' commands.
*/
/*
* To perform these searches, characters are placed into one of three
* classes, and transitions between classes determine word boundaries.
*
* The classes are:
*
* 0 - white space
* 1 - keyword charactes (letters, digits and underscore)
* 2 - everything else
*/
static int stype; /* type of the word motion being performed */
/*
* cls() - returns the class of character at curwin->w_cursor
*
* The 'type' of the current search modifies the classes of characters if a
* 'W', 'B', or 'E' motion is being done. In this case, chars. from class 2
* are reported as class 1 since only white space boundaries are of interest.
*/
static int
cls()
{
register int c;
c = gchar_cursor();
if (c == ' ' || c == '\t' || c == NUL)
return 0;
if (iswordchar(c))
return 1;
/*
* If stype is non-zero, report these as class 1.
*/
return (stype == 0) ? 2 : 1;
}
/*
* fwd_word(count, type, eol) - move forward one word
*
* Returns FAIL if the cursor was already at the end of the file.
* If eol is TRUE, last word stops at end of line (for operators).
*/
int
fwd_word(count, type, eol)
long count;
int type;
int eol;
{
int sclass; /* starting class */
int i;
int last_line;
stype = type;
while (--count >= 0)
{
sclass = cls();
/*
* We always move at least one character, unless on the last character
* in the buffer.
*/
last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
i = inc_cursor();
if (i == -1 || (i == 1 && last_line))
/* started at last char in file */
return FAIL;
if (i == 1 && eol && count == 0) /* started at last char in line */
return OK;
/*
* Go one char past end of current word (if any)
*/
if (sclass != 0)
while (cls() == sclass)
{
i = inc_cursor();
if (i == -1 || (i == 1 && eol && count == 0))
return OK;
}
/*
* go to next non-white
*/
while (cls() == 0)
{
/*
* We'll stop if we land on a blank line
*/
if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
break;
i = inc_cursor();
if (i == -1 || (i == 1 && eol && count == 0))
return OK;
}
}
return OK;
}
/*
* bck_word() - move backward 'count' words
*
* If stop is TRUE and we are already on the start of a word, move one less.
*
* Returns FAIL if top of the file was reached.
*/
int
bck_word(count, type, stop)
long count;
int type;
int stop;
{
int sclass; /* starting class */
stype = type;
while (--count >= 0)
{
sclass = cls();
if (dec_cursor() == -1) /* started at start of file */
return FAIL;
if (!stop || sclass == cls() || sclass == 0)
{
/*
* Skip white space before the word.
* Stop on an empty line.
*/
while (cls() == 0)
{
if (curwin->w_cursor.col == 0 &&
lineempty(curwin->w_cursor.lnum))
goto finished;
if (dec_cursor() == -1) /* hit start of file, stop here */
return OK;
}
/*
* Move backward to start of this word.
*/
if (skip_chars(cls(), BACKWARD))
return OK;
}
inc_cursor(); /* overshot - forward one */
finished:
stop = FALSE;
}
return OK;
}
/*
* end_word() - move to the end of the word
*
* There is an apparent bug in the 'e' motion of the real vi. At least on the
* System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
* motion crosses blank lines. When the real vi crosses a blank line in an
* 'e' motion, the cursor is placed on the FIRST character of the next
* non-blank line. The 'E' command, however, works correctly. Since this
* appears to be a bug, I have not duplicated it here.
*
* Returns FAIL if end of the file was reached.
*
* If stop is TRUE and we are already on the end of a word, move one less.
* If empty is TRUE stop on an empty line.
*/
int
end_word(count, type, stop, empty)
long count;
int type;
int stop;
int empty;
{
int sclass; /* starting class */
stype = type;
while (--count >= 0)
{
sclass = cls();
if (inc_cursor() == -1)
return FAIL;
/*
* If we're in the middle of a word, we just have to move to the end
* of it.
*/
if (cls() == sclass && sclass != 0)
{
/*
* Move forward to end of the current word
*/
if (skip_chars(sclass, FORWARD))
return FAIL;
}
else if (!stop || sclass == 0)
{
/*
* We were at the end of a word. Go to the end of the next word.
* First skip white space, if 'empty' is TRUE, stop at empty line.
*/
while (cls() == 0)
{
if (empty && curwin->w_cursor.col == 0 &&
lineempty(curwin->w_cursor.lnum))
goto finished;
if (inc_cursor() == -1) /* hit end of file, stop here */
return FAIL;
}
/*
* Move forward to the end of this word.
*/
if (skip_chars(cls(), FORWARD))
return FAIL;
}
dec_cursor(); /* overshot - one char backward */
finished:
stop = FALSE; /* we move only one word less */
}
return OK;
}
/*
* bckend_word(count, type) - move back to the end of the word
*
* If 'eol' is TRUE, stop at end of line.
*
* Returns FAIL if start of the file was reached.
*/
int
bckend_word(count, type, eol)
long count;
int type;
int eol;
{
int sclass; /* starting class */
int i;
stype = type;
while (--count >= 0)
{
sclass = cls();
if ((i = dec_cursor()) == -1)
return FAIL;
if (eol && i == 1)
return OK;
/*
* Move backward to before the start of this word.
*/
if (sclass != 0)
{
while (cls() == sclass)
if ((i = dec_cursor()) == -1 || (eol && i == 1))
return OK;
}
/*
* Move backward to end of the previous word
*/
while (cls() == 0)
{
if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
break;
if ((i = dec_cursor()) == -1 || (eol && i == 1))
return OK;
}
}
return OK;
}
static int
skip_chars(cclass, dir)
int cclass;
int dir;
{
while (cls() == cclass)
if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
return TRUE;
return FALSE;
}
/*
* Go back to the start of the word or the start of white space
*/
static void
back_in_line()
{
int sclass; /* starting class */
sclass = cls();
for (;;)
{
if (curwin->w_cursor.col == 0) /* stop at start of line */
break;
--curwin->w_cursor.col;
if (cls() != sclass) /* stop at start of word */
{
++curwin->w_cursor.col;
break;
}
}
}
/*
* Find word under cursor, cursor at end
*/
int
current_word(count, type)
long count;
int type;
{
FPOS start;
FPOS pos;
int inclusive = TRUE;
stype = type;
/*
* When visual area is bigger than one character: Extend it.
*/
if (VIsual_active && !equal(curwin->w_cursor, VIsual))
{
if (lt(curwin->w_cursor, VIsual))
{
if (decl(&curwin->w_cursor) == -1)
return FAIL;
if (cls() == 0)
{
if (bckend_word(count, type, TRUE) == FAIL)
return FAIL;
(void)incl(&curwin->w_cursor);
}
else
{
if (bck_word(count, type, TRUE) == FAIL)
return FAIL;
}
}
else
{
if (incl(&curwin->w_cursor) == -1)
return FAIL;
if (cls() == 0)
{
if (fwd_word(count, type, TRUE) == FAIL)
return FAIL;
(void)oneleft();
}
else
{
if (end_word(count, type, TRUE, TRUE) == FAIL)
return FAIL;
}
}
return OK;
}
/*
* Go to start of current word or white space.
*/
back_in_line();
start = curwin->w_cursor;
/*
* If the start is on white space, find end of word.
* Otherwise find start of next word.
*/
if (cls() == 0)
{
if (end_word(count, type, TRUE, TRUE) == FAIL)
return FAIL;
}
else
{
if (fwd_word(count, type, TRUE) == FAIL)
return FAIL;
/*
* If end is just past a new-line, we don't want to include the first
* character on the line
*/
if (oneleft() == FAIL) /* put cursor on last char of area */
inclusive = FALSE;
else
{
pos = curwin->w_cursor; /* save cursor position */
/*
* If we don't include white space at the end, move the start to
* include some white space there. This makes "d." work better on
* the last word in a sentence. Don't delete white space at start
* of line (indent).
*/
if (cls() != 0)
{
curwin->w_cursor = start;
if (oneleft() == OK)
{
back_in_line();
if (cls() == 0 && curwin->w_cursor.col > 0)
start = curwin->w_cursor;
}
}
curwin->w_cursor = pos; /* put cursor back at end */
}
}
if (VIsual_active)
{
/* should do something when inclusive == FALSE ! */
VIsual = start;
VIsual_mode = 'v';
update_curbuf(NOT_VALID); /* update the inversion */
}
else
{
curbuf->b_op_start = start;
op_motion_type = MCHAR;
op_inclusive = inclusive;
}
return OK;
}
/*
* Find sentence under the cursor, cursor at end.
*/
int
current_sent(count)
long count;
{
FPOS start;
FPOS pos;
int start_blank;
int c;
pos = curwin->w_cursor;
start = pos;
/*
* When visual area is bigger than one character: Extend it.
*/
if (VIsual_active && !equal(curwin->w_cursor, VIsual))
{
if (lt(pos, VIsual))
{
/*
* Do a "sentence backward" on the next character.
* If we end up on the same position, we were already at the start
* of a sentence
*/
if (incl(&curwin->w_cursor) == -1)
return FAIL;
findsent(BACKWARD, 1L);
start = curwin->w_cursor;
if (count > 1)
findsent(BACKWARD, count - 1);
/*
* When at start of sentence: Include blanks in front of sentence.
* Use current_word() to cross line boundaries.
* If we don't end up on a blank or on an empty line, go back to
* the start of the previous sentence.
*/
if (equal(pos, start))
{
current_word(1L, 0);
c = gchar_cursor();
if (c != NUL && !vim_iswhite(c))
findsent(BACKWARD, 1L);
}
}
else
{
/*
* When one char before start of sentence: Don't include blanks in
* front of next sentence.
* Else go count sentences forward.
*/
findsent(FORWARD, 1L);
incl(&pos);
if (equal(pos, curwin->w_cursor))
{
findsent(FORWARD, count);
find_first_blank(&curwin->w_cursor);
}
else if (count > 1)
findsent(FORWARD, count - 1);
decl(&curwin->w_cursor);
}
return OK;
}
/*
* Find start of next sentence.
*/
findsent(FORWARD, 1L);
/*
* If cursor started on blank, check if it is just before the start of the
* next sentence.
*/
while (vim_iswhite(gchar(&pos)))
incl(&pos);
if (equal(pos, curwin->w_cursor))
{
start_blank = TRUE;
/*
* go back to first blank
*/
while (decl(&start) != -1)
{
if (!vim_iswhite(gchar(&start)))
{
incl(&start);
break;
}
}
}
else
{
start_blank = FALSE;
findsent(BACKWARD, 1L);
start = curwin->w_cursor;
}
findsent(FORWARD, count);
/*
* If the blank in front of the sentence is included, exclude the blanks
* at the end of the sentence, go back to the first blank.
*/
if (start_blank)
find_first_blank(&curwin->w_cursor);
else
{
/*
* If there are no trailing blanks, try to include leading blanks
*/
pos = curwin->w_cursor;
decl(&pos);
if (!vim_iswhite(gchar(&pos)))
find_first_blank(&start);
}
if (VIsual_active)
{
VIsual = start;
VIsual_mode = 'v';
decl(&curwin->w_cursor); /* don't include the cursor char */
update_curbuf(NOT_VALID); /* update the inversion */
}
else
{
curbuf->b_op_start = start;
op_motion_type = MCHAR;
op_inclusive = FALSE;
}
return OK;
}
int
current_block(what, count)
int what; /* '(' or '{' */
long count;
{
FPOS old_pos;
FPOS *pos = NULL;
FPOS start_pos;
FPOS *end_pos;
FPOS old_start, old_end;
int inclusive = FALSE;
int other;
old_pos = curwin->w_cursor;
if (what == '{')
other = '}';
else
other = ')';
old_end = curwin->w_cursor; /* remember where we started */
old_start = old_end;
/*
* If we start on '(', '{', ')' or '}', use the whole block inclusive.
*/
if (!VIsual_active || (VIsual.lnum == curwin->w_cursor.lnum &&
VIsual.col == curwin->w_cursor.col))
{
if (what == '{') /* ignore indent */
while (inindent(1))
if (inc_cursor() != 0)
break;
if (gchar_cursor() == what) /* cursor on '(' or '{' */
{
++curwin->w_cursor.col;
inclusive = TRUE;
}
else if (gchar_cursor() == other) /* cursor on ')' or '}' */
inclusive = TRUE;
}
else if (lt(VIsual, curwin->w_cursor))
{
old_start = VIsual;
curwin->w_cursor = VIsual; /* cursor at low end of Visual */
}
else
old_end = VIsual;
/*
* Search backwards for unclosed '(' or '{'.
* put this position in start_pos.
*/
while (count--)
{
if ((pos = findmatch(what)) == NULL)
break;
curwin->w_cursor = *pos;
start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */
}
/*
* Search for matching ')' or '}'.
* Put this position in curwin->w_cursor.
*/
if (pos == NULL || (end_pos = findmatch(other)) == NULL)
{
curwin->w_cursor = old_pos;
return FAIL;
}
curwin->w_cursor = *end_pos;
/*
* Try to exclude the '(', '{', ')' and '}'.
* If the ending '}' is only preceded by indent, skip that indent.
* But only if the resulting area is not smaller than what we started with.
*/
if (!inclusive)
{
incl(&start_pos);
old_pos = curwin->w_cursor;
decl(&curwin->w_cursor);
if (what == '{')
while (inindent(0))
if (decl(&curwin->w_cursor) != 0)
break;
if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor))
{
decl(&start_pos);
curwin->w_cursor = old_pos;
}
}
if (VIsual_active)
{
VIsual = start_pos;
VIsual_mode = 'v';
update_curbuf(NOT_VALID); /* update the inversion */
showmode();
}
else
{
curbuf->b_op_start = start_pos;
op_motion_type = MCHAR;
op_inclusive = TRUE;
}
return OK;
}
int
current_par(type, count)
int type; /* 'p' for paragraph, 'S' for section */
long count;
{
linenr_t start;
linenr_t end;
int white_in_front;
int dir;
int start_is_white;
int retval = OK;
if (type == 'S') /* not implemented yet */
return FAIL;
start = curwin->w_cursor.lnum;
/*
* When visual area is more than one line: extend it.
*/
if (VIsual_active && start != VIsual.lnum)
{
if (start < VIsual.lnum)
dir = BACKWARD;
else
dir = FORWARD;
while (count--)
{
if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
retval = FAIL;
start_is_white = -1;
for (;;)
{
if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
break;
if (start_is_white >= 0 &&
(start_is_white != linewhite(start + dir) ||
startPS(start + (dir > 0 ? 1 : 0), 0, 0)))
break;
start += dir;
if (start_is_white < 0)
start_is_white = linewhite(start);
}
}
curwin->w_cursor.lnum = start;
curwin->w_cursor.col = 0;
return retval;
}
/*
* First move back to the start of the paragraph or white lines
*/
white_in_front = linewhite(start);
while (start > 1)
{
if (white_in_front) /* stop at first white line */
{
if (!linewhite(start - 1))
break;
}
else /* stop at first non-white line of start of paragraph */
{
if (linewhite(start - 1) || startPS(start, 0, 0))
break;
}
--start;
}
/*
* Move past the end of the white lines.
*/
end = start;
while (linewhite(end) && end < curbuf->b_ml.ml_line_count)
++end;
--end;
while (count--)
{
if (end == curbuf->b_ml.ml_line_count)
return FAIL;
++end;
/*
* skip to end of paragraph
*/
while (end < curbuf->b_ml.ml_line_count &&
!linewhite(end + 1) && !startPS(end + 1, 0, 0))
++end;
if (count == 0 && white_in_front)
break;
/*
* skip to end of white lines after paragraph
*/
while (end < curbuf->b_ml.ml_line_count && linewhite(end + 1))
++end;
}
/*
* If there are no empty lines at the end, try to find some empty lines at
* the start (unless that has been done already).
*/
if (!white_in_front && !linewhite(end))
while (start > 1 && linewhite(start - 1))
--start;
if (VIsual_active)
{
VIsual.lnum = start;
VIsual_mode = 'V';
update_curbuf(NOT_VALID); /* update the inversion */
showmode();
}
else
{
curbuf->b_op_start.lnum = start;
op_motion_type = MLINE;
}
curwin->w_cursor.lnum = end;
curwin->w_cursor.col = 0;
return OK;
}
/*
* linewhite -- return TRUE if line 'lnum' is empty or has white chars only.
*/
int
linewhite(lnum)
linenr_t lnum;
{
char_u *p;
p = skipwhite(ml_get(lnum));
return (*p == NUL);
}
static void
find_first_blank(posp)
FPOS *posp;
{
while (decl(posp) != -1)
{
if (!vim_iswhite(gchar(posp)))
{
incl(posp);
break;
}
}
}
void
find_pattern_in_path(ptr, len, whole, skip_comments,
type, count, action, start_lnum, end_lnum)
char_u *ptr; /* pointer to search pattern */
int len; /* length of search pattern */
int whole; /* match whole words only */
int skip_comments; /* don't match inside comments */
int type; /* Type of search; are we looking for a type? a
macro? */
long count;
int action; /* What to do when we find it */
linenr_t start_lnum; /* first line to start searching */
linenr_t end_lnum; /* last line for searching */
{
SearchedFile *files; /* Stack of included files */
SearchedFile *bigger; /* When we need more space */
int max_path_depth = 50;
long match_count = 1;
char_u *pat;
char_u *new_fname;
char_u *curr_fname = curbuf->b_xfilename;
char_u *prev_fname = NULL;
linenr_t lnum;
int depth;
int depth_displayed; /* For type==CHECK_PATH */
int old_files;
int already_searched;
char_u *file_line;
char_u *line;
char_u *p;
char_u *p2 = NUL; /* Init for gcc */
char_u save_char = NUL;
int define_matched;
struct regexp *prog = NULL;
struct regexp *include_prog = NULL;
struct regexp *define_prog = NULL;
int matched = FALSE;
int did_show = FALSE;
int found = FALSE;
int i;
file_line = alloc(LSIZE);
if (file_line == NULL)
return;
reg_magic = p_magic;
if (type != CHECK_PATH)
{
pat = alloc(len + 5);
if (pat == NULL)
goto fpip_end;
sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
prog = vim_regcomp(pat);
vim_free(pat);
if (prog == NULL)
goto fpip_end;
}
reg_ic = FALSE; /* don't ignore case in include and define patterns */
if (*p_inc != NUL)
{
include_prog = vim_regcomp(p_inc);
if (include_prog == NULL)
goto fpip_end;
}
if (type == FIND_DEFINE && *p_def != NUL)
{
define_prog = vim_regcomp(p_def);
if (define_prog == NULL)
goto fpip_end;
}
files = (SearchedFile *)lalloc(max_path_depth * sizeof(SearchedFile), TRUE);
if (files == NULL)
goto fpip_end;
for (i = 0; i < max_path_depth; i++)
{
files[i].fp = NULL;
files[i].name = NULL;
files[i].lnum = 0;
files[i].matched = FALSE;
}
old_files = max_path_depth;
depth = depth_displayed = -1;
lnum = start_lnum;
if (end_lnum > curbuf->b_ml.ml_line_count)
end_lnum = curbuf->b_ml.ml_line_count;
if (lnum > end_lnum) /* do at least one line */
lnum = end_lnum;
line = ml_get(lnum);
for (;;)
{
if (include_prog != NULL && vim_regexec(include_prog, line, TRUE))
{
new_fname = get_file_name_in_path(include_prog->endp[0] + 1,
0, FNAME_EXP);
already_searched = FALSE;
if (new_fname != NULL)
{
/* Check whether we have already searched in this file */
for (i = 0;; i++)
{
if (i == depth + 1)
i = old_files;
if (i == max_path_depth)
break;
if (STRCMP(new_fname, files[i].name) == 0)
{
if (type != CHECK_PATH &&
action == ACTION_SHOW_ALL && files[i].matched)
{
msg_outchar('\n'); /* cursor below last one */
if (!got_int) /* don't display if 'q'
typed at "--more--"
mesage */
{
set_highlight('d'); /* Same as for dirs */
start_highlight();
msg_home_replace(new_fname);
stop_highlight();
MSG_OUTSTR(" (includes previously listed match)");
prev_fname = NULL;
}
}
vim_free(new_fname);
new_fname = NULL;
already_searched = TRUE;
break;
}
}
}
if (type == CHECK_PATH && (action == ACTION_SHOW_ALL ||
(new_fname == NULL && !already_searched)))
{
if (did_show)
msg_outchar('\n'); /* cursor below last one */
else
{
gotocmdline(TRUE); /* cursor at status line */
set_highlight('t'); /* Highlight title */
start_highlight();
MSG_OUTSTR("--- Included files ");
if (action != ACTION_SHOW_ALL)
MSG_OUTSTR("not found ");
MSG_OUTSTR("in path ---\n");
stop_highlight();
}
did_show = TRUE;
while (depth_displayed < depth && !got_int)
{
++depth_displayed;
for (i = 0; i < depth_displayed; i++)
MSG_OUTSTR(" ");
msg_home_replace(files[depth_displayed].name);
MSG_OUTSTR(" -->\n");
}
if (!got_int) /* don't display if 'q' typed
for "--more--" message */
{
for (i = 0; i <= depth_displayed; i++)
MSG_OUTSTR(" ");
set_highlight('d'); /* Same as for directories */
start_highlight();
/*
* Isolate the file name.
* Include the surrounding "" or <> if present.
*/
for (p = include_prog->endp[0] + 1; !isfilechar(*p); p++)
;
for (i = 0; isfilechar(p[i]); i++)
;
if (p[-1] == '"' || p[-1] == '<')
{
--p;
++i;
}
if (p[i] == '"' || p[i] == '>')
++i;
save_char = p[i];
p[i] = NUL;
msg_outstr(p);
p[i] = save_char;
stop_highlight();
if (new_fname == NULL && action == ACTION_SHOW_ALL)
{
if (already_searched)
MSG_OUTSTR(" (Already listed)");
else
MSG_OUTSTR(" NOT FOUND");
}
}
flushbuf(); /* output each line directly */
}
if (new_fname != NULL)
{
/* Push the new file onto the file stack */
if (depth + 1 == old_files)
{
bigger = (SearchedFile *)lalloc(max_path_depth * 2
* sizeof(SearchedFile), TRUE);
if (bigger != NULL)
{
for (i = 0; i <= depth; i++)
bigger[i] = files[i];
for (i = depth + 1; i < old_files + max_path_depth; i++)
{
bigger[i].fp = NULL;
bigger[i].name = NULL;
bigger[i].lnum = 0;
bigger[i].matched = FALSE;
}
for (i = old_files; i < max_path_depth; i++)
bigger[i + max_path_depth] = files[i];
old_files += max_path_depth;
max_path_depth *= 2;
vim_free(files);
files = bigger;
}
}
if ((files[depth + 1].fp = fopen((char *)new_fname, "r"))
== NULL)
vim_free(new_fname);
else
{
if (++depth == old_files)
{
/*
* lalloc() for 'bigger' must have failed above. We
* will forget one of our already visited files now.
*/
vim_free(files[old_files].name);
++old_files;
}
files[depth].name = curr_fname = new_fname;
files[depth].lnum = 0;
files[depth].matched = FALSE;
}
}
}
else
{
/*
* Check if the line is a define (type == FIND_DEFINE)
*/
p = line;
define_matched = FALSE;
if (define_prog != NULL && vim_regexec(define_prog, line, TRUE))
{
/*
* Pattern must be first identifier after 'define', so skip
* to that position before checking for match of pattern. Also
* don't let it match beyond the end of this identifier.
*/
p = define_prog->endp[0] + 1;
while (*p && !isidchar(*p))
p++;
p2 = p;
while (*p2 && isidchar(*p2))
p2++;
save_char = *p2;
*p2 = NUL;
define_matched = TRUE;
}
/*
* Look for a match. Don't do this if we are looking for a
* define and this line didn't match define_prog above.
*/
if ((define_prog == NULL || define_matched) &&
prog != NULL && vim_regexec(prog, p, p == line))
{
matched = TRUE;
/*
* Check if the line is not a comment line (unless we are
* looking for a define). A line starting with "# define" is
* not considered to be a comment line.
*/
if (!define_matched && skip_comments)
{
fo_do_comments = TRUE;
if ((*line != '#' ||
STRNCMP(skipwhite(line + 1), "define", 6) != 0) &&
get_leader_len(line, NULL))
matched = FALSE;
/*
* Also check for a "/ *" or "/ /" before the match.
* Skips lines like "int idx; / * normal index * /" when
* looking for "normal".
*/
else
for (p = line; *p && p < prog->startp[0]; ++p)
if (p[0] == '/' && (p[1] == '*' || p[1] == '/'))
{
matched = FALSE;
break;
}
fo_do_comments = FALSE;
}
}
if (define_matched)
*p2 = save_char;
}
if (matched)
{
#ifdef INSERT_EXPAND
if (action == ACTION_EXPAND)
{
if (depth == -1 && lnum == curwin->w_cursor.lnum)
break;
found = TRUE;
p = prog->startp[0];
while (iswordchar(*p))
++p;
if (add_completion_and_infercase(prog->startp[0],
(int)(p - prog->startp[0]),
curr_fname == curbuf->b_xfilename ? NULL : curr_fname,
FORWARD) == RET_ERROR)
break;
}
else
#endif
if (action == ACTION_SHOW_ALL)
{
found = TRUE;
if (!did_show)
gotocmdline(TRUE); /* cursor at status line */
if (curr_fname != prev_fname)
{
if (did_show)
msg_outchar('\n'); /* cursor below last one */
if (!got_int) /* don't display if 'q' typed
at "--more--" mesage */
{
set_highlight('d'); /* Same as for directories */
start_highlight();
msg_home_replace(curr_fname);
stop_highlight();
}
prev_fname = curr_fname;
}
did_show = TRUE;
if (!got_int)
show_pat_in_path(line, type, TRUE, action,
(depth == -1) ? NULL : files[depth].fp,
(depth == -1) ? &lnum : &files[depth].lnum,
match_count++);
/* Set matched flag for this file and all the ones that
* include it */
for (i = 0; i <= depth; ++i)
files[i].matched = TRUE;
}
else if (--count <= 0)
{
found = TRUE;
if (depth == -1 && lnum == curwin->w_cursor.lnum)
EMSG("Match is on current line");
else if (action == ACTION_SHOW)
{
show_pat_in_path(line, type, did_show, action,
(depth == -1) ? NULL : files[depth].fp,
(depth == -1) ? &lnum : &files[depth].lnum, 1L);
did_show = TRUE;
}
else
{
if (action == ACTION_SPLIT)
{
if (win_split(0, FALSE) == FAIL)
break;
}
if (depth == -1)
{
setpcmark();
curwin->w_cursor.lnum = lnum;
}
else
if (getfile(0, files[depth].name, NULL, TRUE,
files[depth].lnum) > 0)
break; /* failed to jump to file */
}
if (action != ACTION_SHOW)
{
curwin->w_cursor.col = prog->startp[0] - line;
curwin->w_set_curswant = TRUE;
}
break;
}
matched = FALSE;
}
line_breakcheck();
if (got_int)
break;
while (depth >= 0)
{
if (!vim_fgets(line = file_line, LSIZE, files[depth].fp))
{
++files[depth].lnum;
break;
}
fclose(files[depth].fp);
--old_files;
files[old_files].name = files[depth].name;
files[old_files].matched = files[depth].matched;
--depth;
curr_fname = (depth == -1) ? curbuf->b_xfilename
: files[depth].name;
if (depth < depth_displayed)
depth_displayed = depth;
}
if (depth < 0)
{
if (++lnum > end_lnum)
break;
line = ml_get(lnum);
}
}
for (i = 0; i <= depth; i++)
{
fclose(files[i].fp);
vim_free(files[i].name);
}
for (i = old_files; i < max_path_depth; i++)
vim_free(files[i].name);
vim_free(files);
if (type == CHECK_PATH)
{
if (!did_show)
{
if (action != ACTION_SHOW_ALL)
MSG("All included files were found");
else
MSG("No included files");
}
}
else if (!found
#ifdef INSERT_EXPAND
&& action != ACTION_EXPAND
#endif
)
{
if (got_int)
emsg(e_interr);
else if (type == FIND_DEFINE)
EMSG("Couldn't find definition");
else
EMSG("Couldn't find pattern");
}
if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
msg_end();
fpip_end:
vim_free(file_line);
vim_free(prog);
vim_free(include_prog);
vim_free(define_prog);
}
static void
show_pat_in_path(line, type, did_show, action, fp, lnum, count)
char_u *line;
int type;
int did_show;
int action;
FILE *fp;
linenr_t *lnum;
long count;
{
char_u *p;
if (did_show)
msg_outchar('\n'); /* cursor below last one */
else
gotocmdline(TRUE); /* cursor at status line */
if (got_int) /* 'q' typed at "--more--" message */
return;
for (;;)
{
p = line + STRLEN(line) - 1;
if (fp != NULL)
{
/* We used fgets(), so get rid of newline at end */
if (p >= line && *p == '\n')
--p;
if (p >= line && *p == '\r')
--p;
*(p + 1) = NUL;
}
if (action == ACTION_SHOW_ALL)
{
sprintf((char *)IObuff, "%3ld: ", count); /* show match nr */
msg_outstr(IObuff);
set_highlight('n'); /* Highlight line numbers */
start_highlight();
sprintf((char *)IObuff, "%4ld", *lnum); /* show line nr */
msg_outstr(IObuff);
stop_highlight();
MSG_OUTSTR(" ");
}
msg_prt_line(line);
flushbuf(); /* show one line at a time */
/* Definition continues until line that doesn't end with '\' */
if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
break;
if (fp != NULL)
{
if (vim_fgets(line, LSIZE, fp)) /* end of file */
break;
++*lnum;
}
else
{
if (++*lnum > curbuf->b_ml.ml_line_count)
break;
line = ml_get(*lnum);
}
msg_outchar('\n');
}
}
#ifdef VIMINFO
int
read_viminfo_search_pattern(line, fp, force)
char_u *line;
FILE *fp;
int force;
{
char_u *lp;
char_u **pattern;
lp = line;
if (lp[0] == '~')
lp++;
if (lp[0] == '/')
pattern = &search_pattern;
else
pattern = &subst_pattern;
if (*pattern != NULL && force)
vim_free(*pattern);
if (force || *pattern == NULL)
{
viminfo_readstring(lp);
*pattern = strsave(lp + 1);
if (line[0] == '~')
last_pattern = *pattern;
}
return vim_fgets(line, LSIZE, fp);
}
void
write_viminfo_search_pattern(fp)
FILE *fp;
{
if (get_viminfo_parameter('/') != 0)
{
if (search_pattern != NULL)
{
fprintf(fp, "\n# Last Search Pattern:\n");
fprintf(fp, "%s/", (last_pattern == search_pattern) ? "~" : "");
viminfo_writestring(fp, search_pattern);
}
if (subst_pattern != NULL)
{
fprintf(fp, "\n# Last Substitute Search Pattern:\n");
fprintf(fp, "%s&", (last_pattern == subst_pattern) ? "~" : "");
viminfo_writestring(fp, subst_pattern);
}
}
}
#endif /* VIMINFO */
/*
* Give a warning message.
* Use 'w' highlighting and may repeat the message after redrawing
*/
static void
give_warning(message)
char_u *message;
{
(void)set_highlight('w');
msg_highlight = TRUE;
if (msg(message) && !msg_scroll)
{
keep_msg = message;
keep_msg_highlight = 'w';
}
msg_didout = FALSE; /* overwrite this message */
msg_col = 0;
}